/*
 * Middleware
 * Copyright (C) 2010/11
 * Riccardo Pulvirenti, Giuseppe Ravidà, Andrea Tino
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANDABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses
 */

/*!
 * File: worker_descriptor.hpp
 */

#ifndef _WORKER_DESCRIPTOR_HPP_
#define _WORKER_DESCRIPTOR_HPP_

#include <iostream>
#include <string>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/lexical_cast.hpp>

#include "address.hpp"
#include "performance_index.hpp"
#include "xml_translator.hpp"

//-----------------------------------------------------------------------------
// Enum, struct, aliases
namespace middleware {
/*! \struct WorkerCapabilities
 * \brief Describes a Worker's static information.
 *
 * Describes global static capabilities of a Worker. All information stored here
 * should not change in time for a Worker.
 */
typedef struct {
	friend class boost::serialization::access;
	/*!
	 * BOOST serialization function.
	 */
	template<class Archive>
	void serialize(Archive & ar, const unsigned int version) {
		ar & capab_cpu_mhz;
		ar & capab_ram_mb;
		ar & capab_core_num;
		ar & capab_cpu_num;
		ar & capab_hops;
		ar & capab_bandwidth;
	}
	/* Members */
	/*!
	 * Represents the CPU clock frequency of the Worker expressed in MHz.
	 */
	double capab_cpu_mhz; /* CPU MHz */
	/*!
	 * Represents the RAM memory of the Worker expressed in MBytes.
	 */
	double capab_ram_mb; /* RAM in MegaBytes */
	/*!
	 * Represents the number of core per CPU of the Worker.
	 */
	unsigned int capab_core_num; /* Number of cores per processor */
	/*!
	 * Represents the number of CPUs in the Worker.
	 */
	unsigned int capab_cpu_num; /* Number of processors */
	/*!
	 * Represents the number of network hops that separates the current Worker
	 * from all the others. This is just an indicative measure, it is not
	 * supposed to provide a precise information about how far is a Worker, it
	 * is more like an approximation of the mean distance.
	 */
	unsigned int capab_hops; /* INET hop distance */
	/*!
	 * Represents the available bandwidth on the current Worker expressed in
	 * Kbits.
	 */
	double capab_bandwidth; /* Bandwidth in KBit/s */
	/*!
	 * Renders to xml.
	 */
	std::string ToXml() const {
		using std::string;
		string xml = string("<WorkerCapabilities>");
		xml += string("<CpuMhz>");
		xml += boost::lexical_cast<string>(this->capab_cpu_mhz);
		xml += string("</CpuMhz>");
		xml += string("<RamMb>");
		xml += boost::lexical_cast<string>(this->capab_ram_mb);
		xml += string("</RamMb>");
		xml += string("<CoresNum>");
		xml += boost::lexical_cast<string>(this->capab_core_num);
		xml += string("</CoresNum>");
		xml += string("<CpusNum>");
		xml += boost::lexical_cast<string>(this->capab_cpu_num);
		xml += string("</CpusNum>");
		xml += string("<DistanceHops>");
		xml += boost::lexical_cast<string>(this->capab_hops);
		xml += string("</DistanceHops>");
		xml += string("<Bandwidth>");
		xml += boost::lexical_cast<string>(this->capab_bandwidth);
		xml += string("</Bandwidth>");
		xml += string("</WorkerCapabilities>");
		return xml;
	}
} WorkerCapabilities;
/*!
 * Type for worker load (number of pending tasks).
 */
typedef unsigned int WorkerLoad;
}
//-----------------------------------------------------------------------------
// Constants
namespace middleware {
/*!
 * Defines the standard capabilites for a normal machine.
 */
const WorkerCapabilities kStdWorkerCapab = {
		800.0 /* 800 Mhz CPU */,
		512.0 /* 512 MB RAM */,
		2 /* Intel 2-Core */,
		1 /* One CPU */,
		1 /* 5 hops: almost a great local network */,
		5000.0 /* 5 Mbit/s */
};
/*!
 * When not specified, a worker has this performance. This is a null capability.
 */
const WorkerCapabilities kDefWorkerCapab = {
		0.0 /* Mhz CPU */,
		0.0 /* MB RAM */,
		0 /* Cores */,
		0 /* CPUs */,
		0 /* Hops: almost a great local network */,
		0.0 /* Mbit/s */
};
}
//-----------------------------------------------------------------------------
// Class definitions
namespace middleware {
/*! \class WorkerDescriptor
 * \brief Describe a Worker (dynamic and static information).
 *
 * This class is widely used to track the current capabilities and characteristics
 * of a Worker. A Worker has static and dynamic information. Static data are
 * characteristics like the number of CPU, its speed, its internal memory.
 * Dynamic data are represented by all those time-variant information regarding
 * the computetional capabilites like the number of TaskDescriptor in the queue.
 * When considered together, all these information generate a global index called
 * PI (Performance Index) used to compute the current status of a Worker.
 * This class stores all information needed to calculate PI for a particular
 * Worker. PI is used mainly by WorkerDiscovery to get the most suitable Worker
 * for a particular task, a comparison among all PI of every Worker is
 * performed, WorkerDiscovery will choose the Worker having the highest PI.
 */
class WorkerDescriptor : public xml::XmlTranslator {
	friend class Worker;
public:
	/*!
	 * Constructs a WorkerDescriptor located on loopback ip address and invalid
	 * port number. Capabilities are set to kDefWorkerCapab and current status (
	 * elements in the queue) is set to 0.
	 */
	WorkerDescriptor();
	/*!
	 * Constructs a WorkerDescriptor located on the specified address.
	 * Capabilities are set to kDefWorkerCapab and current status (
	 * elements in the queue) is set to 0.
	 * \param addr Address where binding the Worker.
	 */
	explicit WorkerDescriptor(Address addr);
	/*!
	 * Constructs a WorkerDescriptor binding it to a specified address and
	 * also assigning a set of capabilities.
	 * \param addr Address used to bind the Worker.
	 * \param capab WorkerCapabilities assigned to the Worker.
	 */
	WorkerDescriptor(Address addr, WorkerCapabilities capab);
	/*!
	 * Copy constructor.
	 */
	WorkerDescriptor(const WorkerDescriptor& wdesc);
	/*!
	 * Destructor.
	 */
	~WorkerDescriptor();
	/*!
	 * Gets the load/status of the worker associated to this descriptor.
	 * \return Returns the current number of elements in the queue for this
	 * Worker
	 */
	WorkerLoad get_status() const {
		return this->_status;
	}
	/*!
	 * Sets the load/status of the worker associated to this descriptor.
	 * \param status WorkerLoad to set for the Worker.
	 */
	void set_status(WorkerLoad status) {
		this->_status = status;
	}
	/*!
	 * Gets capabilities associated to this Worker.
	 * \return Returns the WorkerCapabilities associated to this Worker.
	 */
	WorkerCapabilities get_capabilities() const {
		return this->_capabilities;
	}
	/*!
	 * Sets capabilities for this Worker.
	 * \param capab WorkerCapabilities to associate.
	 */
	void set_capabilities(WorkerCapabilities capab) {
		this->_capabilities = capab;
	}
	/*!
	 * Gets the address where the Worker is bound to (resides).
	 * \return Returns the Address specifying the location of the Worker.
	 */
	Address get_address() const {
		return this->_address;
	}
	/*!
	 * Sets the address where the Worker will bind to.
	 * \param addr Address to assign to the Worker.
	 */
	void set_address(Address addr) {
		this->_address = addr;
	}
	/*!
	 * Gets the current PI for this Worker.
	 * \return Returns the Perormance Index associated to this Worker at the
	 * current time.
	 */
	calculus::PIType PI() const;
	/*
	 * In order to see if a descriptor is changed, let's overload equality
	 * operator.
	 */
	bool operator==(const WorkerDescriptor& wdesc) const;
	bool operator!=(const WorkerDescriptor& wdesc) const;
	/*!
	 * Implementation of XMLTranslator interface
	 */
	std::string ToXml() const;
private:
	friend class boost::serialization::access;
	/*!
	 * BOOST serialization function.
	 */
	template<class Archive>
	void serialize(Archive & ar, const unsigned int version) {
		ar & _address;
		ar & _capabilities;
		ar & _status;
	}
	/*!
	 * Address.
	 */
	Address _address;
	/*!
	 * Capabilities of this worker.
	 */
	WorkerCapabilities _capabilities;
	/*!
	 * Indicates the load actually sustained by the worker. From now on, we'll
	 * intend status = load. If load is 0 ==> status is free. If load > 0 ==>
	 * status is not free.
	 */
	WorkerLoad _status;
};
} /* middleware */

#endif
